package com.briup.pts.web.interceptor;

import com.briup.pts.config.auth.TokenMap;
import com.briup.pts.config.auth.UserDetails;
import com.briup.pts.config.exception.ServiceException;
import com.briup.pts.config.response.ResultCode;
import com.briup.pts.domain.bean.Account;
import com.briup.pts.mapper.AccountMapper;
import com.briup.pts.util.JwtUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

/**
 * 用于登录认证的拦截器，
 * 需要通过实现HandlerInterceptor接口标识；
 * 本类需要被IOC容器创建对象
 */
@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Value("${jwt.header}")
    private String header;
    @Resource
    private AccountMapper mapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //如果请求方式为OPTIONS，表示跨域请求的预请求，需要直接放行（其内部不携带信息）
        String method = request.getMethod();
        if ("OPTIONS".equals(method)) {
            return true;
        }

        //先获取token
        String token = request.getHeader(header);
        // 对token进行校验，如果token校验的过程中有问题，那么会抛出异常
        // 该异常出现在拦截器后，会被全局异常处理类捕获到，并且进行处理转换
        String username = JwtUtil
                .fieldValue(token, "username", String.class);

        // 从TokenMap中获取对应用户的token进行比较，如果不一致，那么让其重新登录
        String token2 = TokenMap.getToken(username);
        if (!token.equals(token2)) {
            throw new ServiceException(ResultCode.TOKEN_IS_MISS);
        }

        // 将当前登录用户的对象，放入到UserDetails中
        Account account = mapper.findByUsername(username);
        UserDetails.setCurrentUser(account);

        // 如果token校验没有问题，则返回true，继续处理请求
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 当执行本方法时，请求的任务都执行完毕，
        // 即当前线程的任务都执行完毕，所以可以清理ThreadLocal中的数据
        // 避免堆内存溢出的问题
        UserDetails.clear();
    }
}
